import java.util.HashMap;
import java.util.Scanner;

/*** class deals with executing commands from programs that are assigned to specific robots ***/
public class ProgramExecuter {
	
	HashMap<String, ProgramCommand> commands;
	Boxes boxes;
	
	//c'tor -  receives boxes class to work with
	public ProgramExecuter(Boxes boxes){
		commands = new HashMap<String, ProgramCommand>();
		
		add(new MoveCommand());
		add(new PickUpBoxCommand());
		add(new PutBoxDownCommand());
		add(new StopCommand());
		
		this.boxes = boxes;
	}
	
	//add command to hash map
	private void add(ProgramCommand p){
		commands.put(p.getKey(), p);
	}
	
	
	//function receives command robot and file name and executes command
    public void runCommand(String cmd, Robot robot){
		
    	String key = Main.firstWord(cmd);
		//if command exists
		if(commands.containsKey(key)){
			ProgramCommand command = commands.get(key); //receive command class
			if(command.checkFormat(cmd)){      			//check if command is in correct format
				command.execute(cmd, robot); 			//execute command
				return; //end command
			}
		}
		else{
			GUI.errorMessage(robot.getFileName() + " on " + robot.getName() 
												+ ": Error: command not found.");
		}
    }
/***_________________________________________________________________________________***/	
	//abstract class represents a legal command from a program file 
	private abstract class ProgramCommand{
		protected String format;
		//returns first word of command as key for map
		public abstract String getKey();
		//checks if format matches input
		public boolean checkFormat(String input){ return input.matches(format); }
		//execute command
		public abstract void execute(String cmd, Robot r);
	}
	
	/*** _________________________________________________________________________________ ***/
	
	//class is in charge of setting robots speed and heading 
	private class MoveCommand extends ProgramCommand{
		
		public MoveCommand(){
			format = "Move speed (-?\\d+) heading (-?\\d+)";
		}
		//returns first word of command as key for hash map
		public String getKey(){ return "Move"; }
		//execute command
		public void execute(String cmd, Robot r){
			
			Scanner scn = new Scanner(cmd);
			scn.findInLine(format); //scans according to format
			
			int speed = Integer.parseInt(scn.match().group(1));
			int heading = Integer.parseInt(scn.match().group(2));
			r.move(speed, heading);
			
		}
	}
	
	/***______________________________________________________________________________________***/
	
	//class is in charge of stopping robot
	private class StopCommand extends ProgramCommand{
		//c'tor
		public StopCommand(){
			format = "Stop";
		}
		//returns first word of command as key for hash map
		public String getKey(){ return "Stop"; }
		//execute command
		public void execute(String cmd, Robot r){
			r.stop();
		}
	}
	
/***______________________________________________________________________________________***/
	
	//class is in charge of having robot pick up box
	private class PickUpBoxCommand extends ProgramCommand{
		//c'tor
		public PickUpBoxCommand(){
			format = "Pickup (\\w+)";
		}
		//returns first word of command as key for hash map
		public String getKey(){ return "Pickup"; }
		//execute command
		public synchronized void execute(String cmd, Robot r){
			
			//get box name
			Scanner scn = new Scanner(cmd);
			scn.findInLine(format);
			String name = scn.match().group(1);
			
			 //if box exists
			if(boxes.exists(name)) {
				boolean up = r.pickupBox(boxes.getBox(name));
				if(!up){
					GUI.errorMessage(r.getFileName() + " on " + r.getName()  +
							": Error: " + r.getName() + " is unable to pickup " + name);
				}
			}
			else{
				GUI.errorMessage(r.getFileName() + " on " + r.getName()  +
												": Error: no box by that name.");
			}
		}//end execute function
	}

/***______________________________________________________________________________________***/
	
	//class is in charge of having robot pick up box
	private class PutBoxDownCommand extends ProgramCommand{
		//c'tor
		public PutBoxDownCommand(){
			format = "PutBoxDown";
		}
		//returns first word of command as key for hash map
		public String getKey(){ return "PutBoxDown"; }
		//execute command
		public synchronized void execute(String cmd, Robot r){
			//put box down and print error if robot isn't carrying box
			if(!r.putdownBox()) {
				GUI.errorMessage(r.getFileName() + " on " + r.getName()  +
									": Error: " + r.getName() + " has no box.");
			}
		}
	}


}



